/*
 * @Description:  then回调函数可选

 * @version: 
 * @Author: hui.wang01
 * @Date: 2020-12-23 21:02:12
 * @LastEditors: hui.wang01
 */


const PENDING = 'pending' //等待
const FULFILLED = 'fulfilled' // 成功解决
const REJECTED = 'rejected'  // 失败
class MyPromise {
  constructor(executor){
    // tips 1.executor执行报错
    try {
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
  }
  // promise状态    
  // tips: 这种定义属性的方式，在node10.13.0版本上不能正常运行， node 12.6.0可以正常运行！！！！
  status = PENDING

  // 成功后返回的值
  value = undefined

  // 失败的原因
  reason = undefined
  
  // 成功回调
  successCallback = []

  // 失败回调
  failCallback = []
  
  resolve = value => {
    console.log('resolve value:',value)
    // 如果状态不是等待 阻止程序向下执行
    if(this.status != PENDING) return 
    
    // 翻转promise状态
    this.status = FULFILLED    // tips: status要用this.status来访问！！
    
    //保存成功后的值
    this.value = value   // tips: value要用this.value来访问！！

    // 判断成功回调是否存在 如果存在 调用
    // this.successCallback && this.successCallback(this.value)
    while(this.successCallback.length) {  // 判断成功回调是否存在 如果存在 依次调用
      
      // tips: shift() 方法用于把数组的第一个元素从其中删除，并返回第一个元素的值!!!
      // this.successCallback.shift()(this.value) 
      this.successCallback.shift()()  // todo: 为啥加上异常处理后，这里的参数就可以不用传了？
    }
  }

  reject = reason => {
    console.log('reject function')
    // 如果状态不是等待 阻止程序向下执行
    if(this.status != PENDING) return 

    //翻转promise状态
    this.status = REJECTED
    
    // 保存失败原因
    this.reason = reason // tips: reason要用this.reason来访问！！

    // 判断失败回调是否存在 如果存在 调用
    // this.failCallback && this.failCallback(this.reason)
    while(this.failCallback.length) {  // 判断失败回调是否存在 如果存在 依次调用
      
      // tips: shift() 方法用于把数组的第一个元素从其中删除，并返回第一个元素的值!!!
      // this.failCallback.shift()(this.reason) 
      this.failCallback.shift()()  // todo: 为啥加上异常处理后，这里的参数就可以不用传了？
    }
  }

  then = (successCallback, failCallback) => {
    // then回调函数可选，
    // tips:then() 相当于 then(value => value)---成功 !!!
    successCallback = successCallback? successCallback: value => value
    failCallback = failCallback? failCallback: reason => { throw reason}
    let promise2 = new MyPromise((resolve, reject) => {
      if(this.status == FULFILLED){ //执行成功回调
        setTimeout(()=>{   
          try {
            // todo & tips: 用setTimeout的原因是: 要想在调用resolvePromise时拿到promise2对象,必须是在后续代码执行后才能拿到（在return执行后？）???
            let result = successCallback(this.value)
            // resolve(result) // tips2:要实现then方法的链式调用,还要把上一个then方法的回调函数的返回值传给下一个then;
            // 需要判断返回值的类型
            resolvePromise(promise2, result, resolve, reject)
          } catch (err) {
            reject(err)
          } 
        },0)       
      } else if(this.status == REJECTED) {    
        // failCallback(this.reason)    
        // 考虑到回调中可能会报错，进化成下面的样子:
        setTimeout(()=>{   
          try {
            // todo & tips: 用setTimeout的原因是: 要想在调用resolvePromise时拿到promise2对象,必须是在后续代码执行后才能拿到（在return执行后？）???
            let result = failCallback(this.reason)
            // resolve(result) // tips2:要实现then方法的链式调用,还要把上一个then方法的回调函数的返回值传给下一个then;
            // 需要判断返回值的类型
            resolvePromise(promise2, result, resolve, reject)
          } catch (err) {
            reject(err)
          } 
        },0)       
      } else {
        // 保存回调函数
        // this.successCallback = successCallback 
        // this.failCallback = failCallback
        // 同一个promise对象下面的then方法是可以被调用多次的,所以回调函数是一个数组
        // this.successCallback.push(successCallback)  
        // this.failCallback.push(failCallback)
        this.successCallback.push(()=>{
          setTimeout(() => {
            try {          
              let result = successCallback(this.value)
              resolvePromise(promise2, result, resolve, reject)
            }  catch (err) {
              reject(err)
            }
          }, 0)
        })

        this.failCallback.push(()=>{
          setTimeout(() => {
            try {          
              let result = failCallback(this.value)
              resolvePromise(promise2, result, resolve, reject)
            }  catch (err) {
              reject(err)
            }
          }, 0)
        })
      }
    })
    
    return  promise2  // tips1:要实现then方法的链式调用，then方法必须返回Promise对象；
  }

}

// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve 
// 如果是promise对象 查看promsie对象返回的结果 
// 再根据promise对象返回的结果 决定调用resolve 还是调用reject
function resolvePromise(promise2, result, resolve, reject) {
  if(promise2 === result) {
    // tips: return 是为了组织代码继续往下执行
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))  // tips:一定要调用reject!!!
  }
  if (result instanceof MyPromise) { //promise对象
    // result.then(res => resolve(res), reason => reject(reason))
    //可简写成： todo: 简写的时候参数是怎么传递的？？？？
    result.then(resolve, reject)  // tips: 通过result.then拿到当前的promise的状态!!!  
  } else { //普通值
    resolve(result)
  }
}

module.exports = MyPromise